<!DOCTYPE html>
<html>
  <head>
    <title>JavaScript轮播图</title>
    <style type="text/css">
      * {
        margin: 0;
        padding: 0;
      }
      .slide {
        margin: 20px auto;
        width: 600px;
        outline: 10px solid red;
        overflow: hidden;
      }
      .slide-box {
        font-size: 0;
        width: 10000000px;
      }
      .slide img {
        display: inline-block;
        width: auto;
        user-select: none;
      }
    </style>
  </head>
  <body>
    <!-- 视窗容器 -->
    <div class="slide">
      <!-- 滚动容器 -->
      <div class="slide-box"></div>
    </div>
  </body>
</html>
<script>
  const screenWidth = 600;
  let currentHeight = 300;
  let currentScrollLeft = 0;
  let currentIndex = 0;
  const images = [
    { url: "./images/1.jpeg", width: 950, height: 1425 },
    { url: "./images/2.jpeg", width: 640, height: 360 },
    { url: "./images/3.jpeg", width: 950, height: 1425 },
    { url: "./images/4.jpeg", width: 950, height: 633 },
    { url: "./images/5.jpeg", width: 640, height: 461 },
  ];
  const $slide = document.querySelector(".slide");
  const $slideBox = document.querySelector(".slide-box");
  const getCurrentHeight = (index) =>
    (screenWidth / images[index].width) * images[index].height;
  currentHeight = getCurrentHeight(0);

  images.forEach((img) => {
    const $img = document.createElement("img");
    $img.src = img.url;
    $img.style.height = currentHeight + "px";
    $slideBox.appendChild($img);
    img.$el = $img;
  });

  const scroll = (offset = 0, auto = false) => {
    // 清除缓动效果
    $slideBox.style.transition = "none";
    images.forEach((img) => {
      img.$el.style.transition = "none";
    });

    // 如果offset是正的，那么向左，否则向右
    // 目表索引
    let targetIndex = offset > 0 ? currentIndex + 1 : currentIndex - 1;

    if (targetIndex >= images.length) return;
    if (targetIndex < 0) return;

    // 偏移百分比
    const offsetPercent = Math.abs(offset / screenWidth);

    // 目标高度
    let targetHeight = getCurrentHeight(targetIndex);
    // 目标scrollLeft
    let targetScrollLeft = 0;
    for (let i = 0; i < targetIndex; i++) {
      targetScrollLeft += (targetHeight / images[i].height) * images[i].width;
    }

    // 计算偏移
    if (Math.abs(offsetPercent) !== 1) {
      // 要缩放的高度值
      let zoomHeight = targetHeight - currentHeight;
      targetHeight -= (1 - offsetPercent) * zoomHeight;

      // 要移动的距离
      let zoomScrollLeft = targetScrollLeft - currentScrollLeft;
      targetScrollLeft -= (1 - offsetPercent) * zoomScrollLeft;
    } else {
      currentIndex = targetIndex;
      currentHeight = targetHeight;
      currentScrollLeft = targetScrollLeft;
    }

    // 如果是自动补位，加上缓动效果
    if (auto) {
      $slideBox.style.transition = "all .5s";
    }

    images.forEach((img) => {
      if (auto) {
        img.$el.style.transition = "all .5s";
      }
      img.$el.style.height = `${targetHeight}px`;
    });
    $slideBox.style.transform = `translate3d(-${targetScrollLeft}px, 0 ,0)`;
  };

  const Drag = ($el, onMove = () => {}, onOver = {}) => {
    const state = {
      x: 0,
      y: 0,
      playing: false,
    };
    const getSize = (e) => {
      const width = e.clientX - state.x;
      const height = e.clientY - state.y;
      return { width, height };
    };
    $el.addEventListener(
      "mousedown",
      (e) => {
        state.playing = true;
        state.x = e.clientX;
        state.y = e.clientY;
        e.stopPropagation();
        e.preventDefault();
      },
      false
    );
    document.documentElement.addEventListener(
      "mousemove",
      (e) => {
        if (!state.playing) return;
        onMove(getSize(e));
      },
      false
    );
    document.documentElement.addEventListener(
      "mouseup",
      (e) => {
        if (!state.playing) return;
        state.playing = false;
        onOver(getSize(e));
      },
      false
    );
  };
  Drag(
    $slide,
    (size) => {
      if (Math.abs(size.width) < 5) return;
      scroll(
        Math.min(screenWidth - 1, Math.abs(size.width)) *
          (Math.abs(size.width) / -size.width)
      );
    },
    (size) => {
      if (Math.abs(size.width) < 5) return;
      scroll((Math.abs(size.width) / -size.width) * screenWidth, true);
    }
  );
</script>
